package engine;

import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

/**
 * 
 * @author ak2765
 * @author Vika
 *
 */
class WordFrequencies {

	private static class DecreasingKeyComparator implements Comparator<Integer> {

		public int compare(Integer e1, Integer e2) {
			return e2.compareTo(e1);
		}
		
	}
	
	private final Map<String, Integer> positiveFrequenciesIndexedByWords = new HashMap<String, Integer>();
	private final SortedMap<Integer, List<String>> positiveWordsIndexedByFrequenies = new TreeMap<Integer, List<String>>(new DecreasingKeyComparator());

	private final Map<String, Integer> negativeFrequenciesIndexedByWords = new HashMap<String, Integer>();
	private final SortedMap<Integer, List<String>> negativeWordsIndexedByFrequenies = new TreeMap<Integer, List<String>>(new DecreasingKeyComparator());

	private final Map<String, Integer> totalFrequenies = new HashMap<String, Integer>();

	Map<String, Integer> getPositiveWordsFrequencies() {
		return new HashMap<String, Integer>(positiveFrequenciesIndexedByWords);
	}

	Map<String, Integer> getNegativeWordFrequencies() {
		return new HashMap<String, Integer>(negativeFrequenciesIndexedByWords);
	}

	Map<String, Integer> getTotalWordsFrequencies() {
		return new HashMap<String, Integer>(totalFrequenies);
	}

	SortedMap<Integer, List<String>> getNegativeWordsIndexedByFrequencies() {
		return new TreeMap<Integer, List<String>>(
				negativeWordsIndexedByFrequenies);
	}
	
	SortedMap<Integer, List<String>> getPositiveWordsIndexedByFrequencies() {
		return new TreeMap<Integer, List<String>>(
				positiveWordsIndexedByFrequenies);
	}

	/**
	 * @param word
	 */
	public void addNegativeWord(String word) {
		if (this.negativeFrequenciesIndexedByWords.containsKey(word)) {
			this.addAlreadyExistingWord(word, this.negativeFrequenciesIndexedByWords, this.negativeWordsIndexedByFrequenies);
		} else {
			this.addNewWord(negativeFrequenciesIndexedByWords, word, this.negativeWordsIndexedByFrequenies);
		}

		incrementTotalFrequencies(word);
	}

	/**
	 * @return
	 */
	public List<String> getPositiveWords() {
		List<String> positiveWords = new LinkedList<String>();
		positiveWords.addAll(this.positiveFrequenciesIndexedByWords.keySet());
		return positiveWords;
	}

	
	/**
	 * @param word
	 */
	public void addPositiveWord(String word) {
		if (this.positiveFrequenciesIndexedByWords.containsKey(word)) {
			this.addAlreadyExistingWord(word, this.positiveFrequenciesIndexedByWords, this.positiveWordsIndexedByFrequenies);
		} else {
			this.addNewWord(positiveFrequenciesIndexedByWords, word, this.positiveWordsIndexedByFrequenies);
		}

		incrementTotalFrequencies(word);
	}
	
	
	/**
	 * @param word
	 * @param frequencies
	 * @param wordsIndexedByFrequencies
	 */
	private void addAlreadyExistingWord(String word, Map<String, Integer> frequencies, SortedMap<Integer,List<String>> wordsIndexedByFrequencies) {
		int count = frequencies.get(word);
		frequencies.put(word, count + 1);

		incrementWordsIndexedByFrequencies(word, count, wordsIndexedByFrequencies);
	}
	
	/**
	 * @param frequencies
	 * @param word
	 * @param wordsIndexedByFrequencies
	 */
	private void addNewWord(Map<String, Integer> frequencies, String word, SortedMap<Integer,List<String>> wordsIndexedByFrequencies) {
		frequencies.put(word, 1);

		List<String> wordsWithCountOne = wordsIndexedByFrequencies.get(1);

		if (wordsWithCountOne == null) {
			wordsWithCountOne = new LinkedList<String>();
			wordsIndexedByFrequencies.put(1, wordsWithCountOne);
		}
		
		if (wordsWithCountOne.contains(word)) {
			throw new Error("addNewWord is broken (adding word: " + word + ")");
		}
		
		wordsWithCountOne.add(word);
	}

	/**
	 * @param word
	 * @param count
	 * @param wordsIndexedByFrequencies
	 */
	private void incrementWordsIndexedByFrequencies(String word, int count, SortedMap<Integer,List<String>> wordsIndexedByFrequencies) {
		wordsIndexedByFrequencies.get(count).remove(word);
		
		if (wordsIndexedByFrequencies.get(count).isEmpty()) {
			wordsIndexedByFrequencies.remove(count);
		}
		
		if (wordsIndexedByFrequencies.containsKey(count + 1)) {
			wordsIndexedByFrequencies.get(count + 1).add(word);
		} else {
			List<String> negativeWords = new LinkedList<String>();
			negativeWords.add(word);
			wordsIndexedByFrequencies.put(count + 1, negativeWords);
		}
	}


	/**
	 * @param word
	 */
	private void incrementTotalFrequencies(String word) {
		if (totalFrequenies.containsKey(word)) {
			int count = totalFrequenies.get(word);
			totalFrequenies.put(word, count + 1);
		} else {
			totalFrequenies.put(word, 1);
		}
	}

	/**
	 * @param quantityWords
	 * @return
	 */
	public List<String> getMoreRelevantPositiveWords(int quantityWords) {
		List<String> relevantWords = new LinkedList<String>();

		Iterator<Map.Entry<Integer, List<String>>> positiveEntryIterator = this.positiveWordsIndexedByFrequenies
				.entrySet().iterator();

		int count = 0;
		while (positiveEntryIterator.hasNext()) {
			Iterator<String> positiveWords = positiveEntryIterator.next()
					.getValue().iterator();
			while (positiveWords.hasNext() && count < quantityWords) {
				relevantWords.add(positiveWords.next());
				count++;
			}
		}

		return relevantWords;
	}

	/**
	 * @param word
	 * @return
	 */
	public int getPositiveFrequencyForWord(String word) {
		Integer frequency = this.positiveFrequenciesIndexedByWords.get(word);
		if (frequency == null) {
			return 0;
		}
		return frequency;
	}

	/**
	 * @param word
	 * @return
	 */
	public int getNegativeFrequencyForWord(String word) {
		Integer frequency = this.negativeFrequenciesIndexedByWords.get(word);
		if (frequency == null) {
			return 0;
		}
		return frequency;
	}
}
